"""
异常处理 介绍

捕捉异常可以使用try/except语句。try/except语句用来检测try语句块中的错误，从而让except语句捕获异常信息并处理。
try的工作原理是：当开始一个try语句后，python就在当前程序的上下文中作标记，这样当异常出现时就可以回到这里，try子句先执行，接下来会发生什么依赖于执行
时是否出现异常：
    ① 如果当try后的语句执行时发生异常，python就跳回到try并执行第一个匹配该异常的except子句，异常处理完毕，控制流就通过整个try语句（除非在处理异
       常时又引发新的异常）；
    ② 如果在try后的语句里发生了异常，却没有匹配的except子句，异常将被递交到上层的try，或者到程序的最上层（这样将结束程序，并打印默认的出错信息）；
    ③ 如果在try子句执行时没有发生异常，python将执行else语句后的语句（如果有else的话），然后控制流通过整个try语句；
    ④ 如果含有 finally 语句，无论是否发生异常都将执行最后的代码；
"""

# 1. 打开一个文件，在该文件中的内容写入内容，且并未发生异常
import os
try:
    fh = open("testfile.txt", "w")
    fh.write("这是一个测试文件，用于测试异常!!")
except IOError:
    print("Error: 没有找到文件或读取文件失败")
#  如果没有异常执行这块代码（如果有else的话）
else:
    print("内容写入文件成功")
    fh.close()
    # os.remove(path)：删除文件 path；os.removedirs(path)：递归地删除目录。如果子目录被成功删除， removedirs() 将会删除父目录；
    # os.rmdir(path)：删除目录 path，要求path必须是个空目录
    os.remove('testfile.txt')
    print("文件成功删除")

# 2. 它打开一个文件，使用默认的读模式。然后在该文件中的内容写入内容，将会发生异常
try:
    fh = open("testfile2.txt")
    fh.write("这是一个测试文件，用于测试异常!!")
except IOError:
    print("Error: 文件操作失败，请确认打开文件的模式是否正确")

# 3. 使用except而不带任何异常类型。这捕获所有发生的异常。但这不是一个很好的方式，我们不能通过该程序识别出具体的异常信息
try:
     aa = 1
     bb = 0
     print(aa / bb)
except:
    print("发生异常")

# 4. 使用except带多种异常类型：except(Exception1[, Exception2[,...ExceptionN]]])
try:
    aa = 1
    bb = 0
    print(aa / bb)
except(IOError, ZeroDivisionError):
    print("发生异常2")
else:
    print("操作成功2")

# 5. try-finally 语句。无论是否发生异常都将执行最后的代码
# 使用 test 用于观察执行顺序
test = 0
try:
    aa = 1
    bb = 0
    test += 1
    print(test)
    print(aa / bb)
    print(test)
except(IOError, ZeroDivisionError):
    print("发生异常3。test值为 %d" % test)
    test += 1
    print(test)
else:
    print("操作成功3")
finally:
    test = 1
    print("无论是否发生异常都将执行3")

# 6. 异常的参数。一个异常可以带上参数，可作为输出的异常信息参数，你可以通过except语句来捕获异常的参数。变量接收的异常值通常包含在异常的语句中。在元
# 组的表单中变量可以接收一个或者多个值。元组通常包含错误字符串，错误数字，错误位置


def temp_convert(var):
    try:
        return int(var)
    except ValueError as Argument:
        print("参数没有包含数字：", Argument)


# 调用函数
temp_convert("xyz")


# 7. 主动抛出异常。使用 raise 语句抛出一个指定的异常：raise [Exception [, args [, traceback]]]
x = 10
if x > 5:
    x__format = 'x 不能大于 5。x 的值为: {}'.format(x)
    print(x__format)
    # raise Exception(x__format)

# 8. 用户自定义异常。通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类，可以直接继承，或者间接继承


class MyError(Exception):

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


# 注意：为了测试效果，需要注释掉 7. 中抛出的异常
try:
    raise MyError(2*2)
except MyError as e:
    print('My exception occurred, value:', e.value)
    # 如果还要抛出本次异常，可以使用 raise
    raise
